
#include "Task.hpp"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <iostream>
const int processnum = 10;
std::vector<task_t> tasks;
// 先描述
class channel // 将管道和进程联系起来，描述的过程
{
public:
  channel(int cmdfd, int slaverid, const std::string &processname)
      : _cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
  {
  }

public:
  int _cmdfd;               // 发送任务的文件描述符
  pid_t _slaverid;          // 子进程的PID
  std::string _processname; // 子进程的名字 -- 方便我们打印日志
};
void slaver()
{
  while (true)
  {
    int cmdcode = 0;
    int n = read(0, &cmdcode, sizeof(int)); // 如果父进程不给子进程发送数据，子进程会进行阻塞等待
    if (n == sizeof(int))
    {
      std::cout << "slaver say@ get a command:" << getpid() << " : cmdcode" << cmdcode << std::endl;
      if (cmdcode >= 0 && cmdcode < tasks.size())
        tasks[cmdcode](); // 执行当前函数
    }
    if (n == 0)
      break;
  }
}
void InitProcessPool(std::vector<channel> *channels)
{
  // version 2: 确保每一个子进程都只有一个写端
  std::vector<int> oldfds;
  for (int i = 0; i < processnum; i++) // 创建十个进程
  {
    int pipefd[2]; // 临时空间
    int n = pipe(pipefd);
    assert(!n); // 演示就可以
    (void)n;

    pid_t id = fork();
    if (id == 0) // child
    {
      // 父进程，第一次占用3，4管道，子进程继承父进程管道，父进程每次关闭读端，下次创建还是占用3，但是写端4已经被占用，所以
      // 后面逐渐+1
      // std::cout << "child: " << getpid() << " close history fd: ";
      // for (auto fd : oldfds)
      // {
      //   std::cout << fd << " ";
      //   close(fd);
      // }
      // std::cout << "\n";

      close(pipefd[1]);
      dup2(pipefd[0], 0); // 就是将pipefd[0]管道中的内容赋值给0输入端，然后read就可以直接从管道中读取
      close(pipefd[0]);
      slaver(); // 子进程执行任务
      // std::cout << "process : " << getpid() << " quit" << std::endl;
      //  slaver(pipefd[0]);
      exit(0);
    }
    // father
    close(pipefd[0]); // 关闭读端

    // 添加channel字段了
    std::string name = "process-" + std::to_string(i);
    channels->push_back(channel(pipefd[1], id, name));
    oldfds.push_back(pipefd[1]);

    sleep(1);
  }
}

void ctrlSlaver(const std::vector<channel> &channels)
{
  while (true)
  {
    // 1.选择任务
    int cmdcode = rand() % tasks.size(); // 任务码
    // 2.选择进程，选择子进程并将任务码信息发送给子进程
    int processpos = rand() % channels.size();
    std::cout << "father say:" << "cmdcode:" << cmdcode << "already sendto " << channels[processpos]._slaverid << "processname: "
              << channels[processpos]._processname << std::endl;
    // 3.发送任务
    write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode)); // 将任务码写到对应子进程的id中
    sleep(1);
  }
}
void QuitProcess(const std::vector<channel> &channels)
{
  for (auto &c : channels)
  {
    close(c._cmdfd); // 关掉每个子进程的读端
    // 回收子进程
    waitpid(c._slaverid, nullptr, 0);
  }
}
int main()
{
  LoakTask(&tasks);                       // 将所有的任务存放在这个数组中，函数指针数组
  srand(time(nullptr) ^ getpid() ^ 1023); // 种一个随机数种子
  // 在组织
  std::vector<channel> channels;
  // 1. 初始化 --- bug?? -- 找一下这个问题在哪里？然后提出一些解决方案！
  // 初始化，每个子进程都有自己的管道，然后父进程通过channel与子进程进行交互，通过管道发送信息
  // 然后控制每个子进程接受信息，然后完成对应的任务
  InitProcessPool(&channels);
  // 2.控制子进程
  ctrlSlaver(channels);
  // 3.清理收尾
  QuitProcess(channels);
  return 0;
}